package View;

/**
 * This class inherits the JFrame populates the frame with the general view
 * of the building such as the floors, the elevators and their status
 * It also contains the inside view of the elevator panel such as the floor buttons
 * Each floor has an up and down button for the floor calls by the user
 * 
 * @author Varun Maryada, Sanat Dixit, Steve Tannahill
 */

import java.awt.*;

import java.awt.event.*;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ConcurrentHashMap;

import javax.swing.*;

import Dispatcher.ElevatorStatus;
import Dispatcher.GroupDispatcher;
import Dispatcher.UpdateThread;

@SuppressWarnings("serial")
public class BuildingGUI extends JFrame implements ActionListener, Observer {

	private int floors = 10;
	private int elevators = 10;

	// FLOORS is actual number of floors
	// GUI has FLOORS+1.. Top row is for labels
	// FLOORS+1-row is to access current floor

	private GroupDispatcher gd;
	private JPanel grid;
	private ArrayList<GridLabel> labelList;
	private ConcurrentHashMap<Integer, ElevatorStatus> currentStatus;
	
	/**
	 * Constructs the building view containing floors and inside view of
	 * elevators
	 * 
	 * @param gd
	 */
	public BuildingGUI(GroupDispatcher gd) {
		super("Elevator Control System");
		this.gd = gd;
		this.currentStatus = new ConcurrentHashMap<Integer, ElevatorStatus>();
		labelList= new ArrayList<GridLabel>();
		this.setSize(1200, 700);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.grid = new JPanel();
		this.buildGrid();
		gd.addObserver(this);
	}

	/**
	 * Builds a grid layout
	 */
	public void buildGrid() {
		Container cp = this.getContentPane();
		cp.setLayout(new BorderLayout());
		grid.setLayout(new GridLayout(floors + 1, elevators + 2));
		cp.add(grid, BorderLayout.CENTER);
		paintGrid();
	}

	public void paintGrid() {
		for (int i = 0; i < floors + 1; i++) {
			for (int j = 0; j < elevators + 2; j++) {
				this.filltopLabel(grid, i, j);
				this.fillGrid(grid, i, j);
			}
		}
	}
	
	/**
	 * Adds the Elevators and floor numbers to the Content Pane
	 * 
	 * @param grid
	 * @param row
	 */
	public void fillGrid(Container grid, int row, int column) {
		if(row>0 && column==0){
			this.grid.add(new JLabel(""+(floors + 1 - row), JLabel.CENTER));
		}
		else if(row>0 && column==1){
			grid.add(new FloorGUI(floors + 1 - row, floors, gd));
		}
		else if(row>0 && column>1){
			GridLabel label = new GridLabel("",row, column);
			label.setOpaque(true);
			label.setBackground(Color.LIGHT_GRAY);
			label.setBorder(BorderFactory.createLineBorder(Color.BLACK));
			labelList.add(label);
			this.grid.add(label);
		}
	}

	/**
	 * Creates the Labels and adds them to the grid
	 * 
	 * @param grid
	 * @param row
	 */
	public void filltopLabel(Container grid, int row, int column) {
		if(row==0 && column == 0){
			grid.add(new JLabel("Floors", JLabel.CENTER));
		}
		else if(row ==0 && column == 1){
			grid.add(new JLabel("", JLabel.CENTER));
		}
		else if(row==0 && column>1){
			grid.add(new JLabel("Elevator-"+(column-1), JLabel.CENTER));
		}
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {

	}

	@SuppressWarnings("unchecked")
	@Override
	public void update(Observable arg0, Object arg1) {
		//if a new elevator registered, create a new elevator view
		if (arg1 instanceof HashMap<?, ?>) {
			int id = 0;
			PrintWriter pw = null;
			HashMap<Integer, PrintWriter> map = (HashMap) arg1;
			id = (Integer) map.keySet().toArray()[0];
			pw = map.get(id);
			this.createNewElevatorView(id, pw);
			this.enableElevators();
			this.updateElevatorPositions();
			
		}
		//update elevator positions in floor view
		else if (arg1 instanceof ConcurrentHashMap<?, ?>) {
			this.currentStatus = (ConcurrentHashMap<Integer, ElevatorStatus>) arg1;
			this.enableElevators();
			this.updateElevatorPositions();
		}
		else {
			System.out.println("Received unknown update from dispatcher");
		}
		
	}
	
	public void createNewElevatorView(int id, PrintWriter pw) {
		if (id > 0 && pw != null) {
			ElevatorGUI elevatorView = new ElevatorGUI(id , pw);
			elevatorView.setVisible(true);
		}
	}
	
	public void enableElevators(){
		for (int i : this.currentStatus.keySet()) {
				ElevatorStatus value = this.currentStatus.get(i);
				int eleId = value.getId();
				this.setElevatorEnabled(eleId);
		}
	}
	
	public void setElevatorEnabled(int elevatorId) {
		for (int j = 0; j < labelList.size(); j++) {
			GridLabel e = labelList.get(j);
			if (e.get_Y() == (elevatorId + 1)) {
				e.setBackground(Color.DARK_GRAY);
			}
		}
	}
	
	public void changeElevatorPosition(int elevatorId, int currentFloor, int state) {
		for (int j = 0; j < labelList.size(); j++) {
			GridLabel e = labelList.get(j);
			if (e.get_Y() == (elevatorId + 1) && e.get_X() == (floors + 1 -currentFloor)) {
				if (state == 0 || state == 1) {
					e.setBackground(Color.WHITE);
				}
				else if (state == 2 || state == 3) {
					e.setBackground(Color.GREEN);
				}
				else if (state == 4) {
					e.setBackground(Color.RED);
				}
				else {
					//Should never go here, but who knows?
					e.setBackground(Color.WHITE);
				}
			}
		}
	}

	public void setElevatorDisabled(int elevatorId) {
		for (int j = 0; j < labelList.size(); j++) {
			GridLabel e = labelList.get(j);
			if (e.get_Y() == (elevatorId + 1)) {
				e.setBackground(Color.LIGHT_GRAY);
			}
		}
	}
	/**
	 * 
	 */
	public void updateElevatorPositions(){
		for (int i : this.currentStatus.keySet()) {
			ElevatorStatus value = this.currentStatus.get(i);
			int eleId = value.getId();
			int floor = value.getCurrentFloor();
			int state = value.getState();
			this.changeElevatorPosition(eleId, floor, state);
		}
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		GroupDispatcher gd = new GroupDispatcher();

		BuildingGUI building = new BuildingGUI(gd);
		building.setVisible(true);
		
		System.out.println("Started dispatcher");
		// create and start an update thread if needed
		UpdateThread ut = new UpdateThread(gd);
		ut.start();
		
		try {
			ut.join();
		} catch (InterruptedException e) {
			System.out.println("Update Thread Interrupted");
		}
		
		gd.finalize();
	}
}
